/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { findIndex } from 'lodash';
import {
  Button,
  Callout,
  Classes,
  FormGroup,
  InputGroup,
  Intent,
  Position
} from '@blueprintjs/core';
import PasswordMask from 'react-password-mask';
import Select from 'react-select';
import { AutoComplete } from 'antd';
import { Form, Validation } from 'calidation';

// Services
import {
  ConnectionService,
  SchemaService
} from '../../../../../../../services';

// UI
import { Loading } from '../../../../../../UI';

// Utils
import { Saiku, Settings } from '../../../../../../../utils';

// Constants
const { SAIKU_COLOR } = Settings;
const OPTIONS_CONNECTION_TYPE = [
  {
    value: 'MONDRIAN',
    label: 'Mondrian'
  },
  {
    value: 'XMLA',
    label: 'XMLA'
  }
];
const OPTIONS_JDBC_URL = Settings.JDBC.URL;
const OPTIONS_JDBC_DRIVER = Settings.JDBC.DRIVER;
const OPTIONS_SECURITY = [
  {
    value: 'NONE',
    label: 'NONE'
  },
  {
    value: 'ONE2ONE',
    label: 'One To One Mapping'
  },
  {
    value: 'PASSTHROUGH',
    label: 'Passthrough (for XMLA)'
  }
];
const SCHEMA_ERROR_MSG = 'Error fetching data from schemas';
const styleAutoCompleteError = {
  border: '1px solid #db3737',
  borderRadius: '4px'
};

class MondrianForm extends Component {
  _isMounted = false;

  constructor(props) {
    super(props);

    this.state = {
      initialValues: {
        connectionType: OPTIONS_CONNECTION_TYPE[0]
      },
      schemas: [],
      username: '',
      password: '',
      securityType: OPTIONS_SECURITY[0],
      isInvalidConnectionName: false,
      loading: false,
      loadingSchemas: false
    };

    this.formValidationConfig = {
      connectionName: {
        isRequired: 'Name field is required'
      },
      connectionType: {
        isRequired: 'Connection Type field is required'
      },
      jdbcUrl: {
        isRequired: 'JDBC URL field is required'
      },
      jdbcDriver: {
        isRequired: 'JDBC Driver field is required'
      },
      schema: {
        isRequired: 'Schema field is required'
      }
    };
  }

  componentWillMount() {
    const { editMode, dataSourceData } = this.props;

    if (editMode) {
      const {
        connectionname,
        jdbcurl,
        driver,
        schema,
        username,
        password,
        security_type
      } = dataSourceData;

      this.setState(
        {
          initialValues: {
            connectionName: connectionname,
            connectionType: OPTIONS_CONNECTION_TYPE[0],
            jdbcUrl: jdbcurl ? jdbcurl : '',
            jdbcDriver: driver ? driver : '',
            schema: schema ? { value: schema, label: schema } : ''
          },
          username: username ? username : '',
          password: password ? password : ''
        },
        () => {
          if (security_type === 'one2one') {
            this.setState({ securityType: OPTIONS_SECURITY[1] });
          } else if (security_type === 'passthrough') {
            this.setState({ securityType: OPTIONS_SECURITY[2] });
          } else {
            this.setState({ securityType: OPTIONS_SECURITY[0] });
          }
        }
      );
    }
  }

  componentDidMount() {
    this._isMounted = true;
    this.callApiGetSchemas();
  }

  componentWillUnmount() {
    this._isMounted = false;
    SchemaService.cancelRequest();
  }

  callApiGetSchemas = () => {
    this.setState({
      loadingSchemas: true,
      error: false
    });

    SchemaService.getSchemas()
      .then(res => {
        if (this._isMounted && res.status === 200) {
          const { data } = res;

          this.setState({
            schemas: data.map(schema => {
              return {
                value: schema.name,
                label: schema.name
              };
            }),
            loadingSchemas: false
          });
        } else {
          if (this._isMounted) {
            this.setState({ loadingSchemas: false });
            Saiku.toasts(Position.TOP_RIGHT).show({
              icon: 'error',
              intent: Intent.DANGER,
              message: SCHEMA_ERROR_MSG
            });
          }
        }
      })
      .catch(error => {
        if (this._isMounted) {
          this.setState({ loadingSchemas: false });
          Saiku.toasts(Position.TOP_RIGHT).show({
            icon: 'error',
            intent: Intent.DANGER,
            message: SCHEMA_ERROR_MSG
          });
        }
      });
  };

  handleChangeField = event => {
    this.setState({
      [event.target.name]: event.target.value
    });
  };

  onFilterOption = (inputValue, option) => {
    return (
      option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !==
      -1
    );
  };

  handleSubmit = ({ fields, errors, isValid }) => {
    if (isValid) {
      const { editMode, dataSourceData, getDataSources, onClose } = this.props;
      const { username, password, securityType } = this.state;
      const {
        connectionName,
        connectionType,
        jdbcUrl,
        jdbcDriver,
        schema
      } = fields;
      let data = {};

      this.setState({ loading: true });

      ConnectionService.getDataSources()
        .then(res => {
          if (res.status === 200) {
            const connectionIndex = findIndex(res.data, {
              connectionname: connectionName
            });

            if (
              (!editMode && connectionIndex !== -1) ||
              (editMode &&
                connectionIndex !== -1 &&
                connectionName !== dataSourceData.connectionname)
            ) {
              this.setState({
                isInvalidConnectionName: true,
                loading: false
              });
            } else {
              if (!editMode) {
                data = {
                  connectionname: connectionName,
                  connectiontype: connectionType.value,
                  jdbcurl: jdbcUrl,
                  driver: jdbcDriver,
                  schema: schema.value,
                  username,
                  password
                };

                if (securityType.value === 'ONE2ONE') {
                  data.security_type = 'one2one';
                } else if (securityType.value === 'PASSTHROUGH') {
                  data.security_type = 'passthrough';
                } else {
                  data.security_type = null;
                }

                ConnectionService.addDataSource(data)
                  .then(res => {
                    this.setState({ loading: false });

                    if (res.status === 200) {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'tick',
                        intent: Intent.SUCCESS,
                        message: 'Data source added'
                      });

                      getDataSources();
                      onClose();
                    } else {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'error',
                        intent: Intent.DANGER,
                        message: res.statusText
                      });
                    }
                  })
                  .catch(error => {
                    Saiku.toasts(Position.TOP_RIGHT).show({
                      icon: 'error',
                      intent: Intent.DANGER,
                      message: 'Something went wrong'
                    });
                  });
              } else {
                const { id } = dataSourceData;
                data = {
                  id,
                  connectionname: connectionName,
                  connectiontype: connectionType.value,
                  jdbcurl: jdbcUrl,
                  driver: jdbcDriver,
                  schema: schema.value,
                  username,
                  password
                };

                if (securityType.value === 'ONE2ONE') {
                  data.security_type = 'one2one';
                } else if (securityType.value === 'PASSTHROUGH') {
                  data.security_type = 'passthrough';
                } else {
                  data.security_type = null;
                }

                ConnectionService.updateDataSource(id, data)
                  .then(res => {
                    this.setState({ loading: false });

                    if (res.status === 200) {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'tick',
                        intent: Intent.SUCCESS,
                        message: 'Data source updated'
                      });

                      getDataSources();
                      onClose();
                    } else {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'error',
                        intent: Intent.DANGER,
                        message: res.statusText
                      });
                    }
                  })
                  .catch(error => {
                    Saiku.toasts(Position.TOP_RIGHT).show({
                      icon: 'error',
                      intent: Intent.DANGER,
                      message: 'Something went wrong'
                    });
                  });
              }
            }
          } else {
            this.setState({ loading: false });
            Saiku.toasts(Position.TOP_RIGHT).show({
              icon: 'error',
              intent: Intent.DANGER,
              message: 'Something went wrong'
            });
          }
        })
        .catch(error => {
          this.setState({ loading: false });
          Saiku.toasts(Position.TOP_RIGHT).show({
            icon: 'error',
            intent: Intent.DANGER,
            message: 'Something went wrong'
          });
        });
    }
  };

  renderErrorInvalidConnectionName() {
    return (
      <Callout
        className="m-b-20"
        intent={Intent.DANGER}
        style={{ color: '#ee5342' }}
      >
        The connection name already exists! Enter another name...
      </Callout>
    );
  }

  renderForm() {
    const { editMode, connType, onClose } = this.props;
    const {
      initialValues,
      schemas,
      username,
      password,
      // securityType,
      isInvalidConnectionName,
      loading
    } = this.state;

    return (
      <Form onSubmit={this.handleSubmit} style={{ margin: 0 }}>
        <Validation
          config={this.formValidationConfig}
          initialValues={initialValues}
        >
          {({ fields, errors, submitted, setField }) => (
            <Fragment>
              <div className={Classes.DIALOG_BODY}>
                {isInvalidConnectionName &&
                  this.renderErrorInvalidConnectionName()}

                <FormGroup
                  label="Name"
                  labelFor="connectionName"
                  intent={
                    submitted && errors.connectionName
                      ? Intent.DANGER
                      : Intent.NONE
                  }
                  helperText={
                    submitted && errors.connectionName
                      ? errors.connectionName
                      : ''
                  }
                >
                  <InputGroup
                    name="connectionName"
                    intent={
                      submitted && errors.connectionName
                        ? Intent.DANGER
                        : Intent.NONE
                    }
                    value={fields.connectionName}
                    autoFocus={!editMode}
                    onChange={event =>
                      setField({ connectionName: event.target.value })
                    }
                  />
                </FormGroup>
                <FormGroup
                  label="Connection Type"
                  labelFor="connectionType"
                  intent={
                    submitted && errors.connectionType
                      ? Intent.DANGER
                      : Intent.NONE
                  }
                  helperText={
                    submitted && errors.connectionType
                      ? errors.connectionType
                      : ''
                  }
                >
                  <Select
                    name="connectionType"
                    options={OPTIONS_CONNECTION_TYPE}
                    value={fields.connectionType}
                    onChange={value => {
                      connType(value.value);
                      setField({ connectionType: value });
                    }}
                    styles={{
                      control: (styles, state) => ({
                        ...styles,
                        borderWidth: state.isFocused
                          ? '1px'
                          : submitted && errors.connectionType
                          ? '0px'
                          : '1px',
                        borderStyle: state.isFocused
                          ? 'solid'
                          : submitted && errors.connectionType
                          ? 'none'
                          : 'solid',
                        borderColor: state.isFocused
                          ? SAIKU_COLOR
                          : submitted && errors.connectionType
                          ? '#fff'
                          : '#ccc',
                        boxShadow: state.isFocused
                          ? `0 0 0 1px ${SAIKU_COLOR}`
                          : submitted && errors.connectionType
                          ? `0 0 0 0 rgba(219, 55, 55, 0),
                             0 0 0 0 rgba(219, 55, 55, 0),
                             inset 0 0 0 1px #db3737,
                             inset 0 0 0 1px rgba(16, 22, 26, 0.15),
                             inset 0 1px 1px rgba(16, 22, 26, 0.2)`
                          : null,
                        ':hover': {
                          ...styles[':hover'],
                          borderColor: state.isFocused ? SAIKU_COLOR : '#ccc'
                        }
                      }),
                      menu: (styles, state) => ({
                        ...styles,
                        zIndex: '2003'
                      }),
                      option: (styles, state) => ({
                        ...styles,
                        backgroundColor: state.isSelected ? SAIKU_COLOR : null,
                        ':hover': {
                          ...styles[':hover'],
                          backgroundColor: state.isSelected
                            ? SAIKU_COLOR
                            : '#e1ddff'
                        },
                        ':active': {
                          ...styles[':active'],
                          backgroundColor: state.isSelected ? SAIKU_COLOR : null
                        }
                      })
                    }}
                    isSearchable
                  />
                </FormGroup>
                <FormGroup
                  label="JDBC URL"
                  labelFor="jdbcUrl"
                  intent={
                    submitted && errors.jdbcUrl ? Intent.DANGER : Intent.NONE
                  }
                  helperText={submitted && errors.jdbcUrl ? errors.jdbcUrl : ''}
                >
                  <AutoComplete
                    name="jdbcUrl"
                    dataSource={OPTIONS_JDBC_URL}
                    placeholder="jdbc:mysql://localhost/foodmart"
                    filterOption={this.onFilterOption}
                    value={fields.jdbcUrl}
                    onChange={value => setField({ jdbcUrl: value })}
                    style={
                      submitted && errors.jdbcUrl ? styleAutoCompleteError : {}
                    }
                    allowClear
                  />
                </FormGroup>
                <FormGroup
                  label="JDBC Driver"
                  labelFor="jdbcDriver"
                  intent={
                    submitted && errors.jdbcDriver ? Intent.DANGER : Intent.NONE
                  }
                  helperText={
                    submitted && errors.jdbcDriver ? errors.jdbcDriver : ''
                  }
                >
                  <AutoComplete
                    name="jdbcDriver"
                    dataSource={OPTIONS_JDBC_DRIVER}
                    placeholder="com.mysql.jdbc.Driver"
                    filterOption={this.onFilterOption}
                    value={fields.jdbcDriver}
                    onChange={value => setField({ jdbcDriver: value })}
                    style={
                      submitted && errors.jdbcUrl ? styleAutoCompleteError : {}
                    }
                    allowClear
                  />
                </FormGroup>
                <FormGroup
                  label="Schema"
                  labelFor="schema"
                  intent={
                    submitted && errors.schema ? Intent.DANGER : Intent.NONE
                  }
                  helperText={submitted && errors.schema ? errors.schema : ''}
                >
                  <Select
                    name="schema"
                    options={schemas}
                    value={fields.schema}
                    onChange={value => setField({ schema: value })}
                    styles={{
                      control: (styles, state) => ({
                        ...styles,
                        borderWidth: state.isFocused
                          ? '1px'
                          : submitted && errors.schema
                          ? '0px'
                          : '1px',
                        borderStyle: state.isFocused
                          ? 'solid'
                          : submitted && errors.schema
                          ? 'none'
                          : 'solid',
                        borderColor: state.isFocused
                          ? SAIKU_COLOR
                          : submitted && errors.schema
                          ? '#fff'
                          : '#ccc',
                        boxShadow: state.isFocused
                          ? `0 0 0 1px ${SAIKU_COLOR}`
                          : submitted && errors.schema
                          ? `0 0 0 0 rgba(219, 55, 55, 0),
                             0 0 0 0 rgba(219, 55, 55, 0),
                             inset 0 0 0 1px #db3737,
                             inset 0 0 0 1px rgba(16, 22, 26, 0.15),
                             inset 0 1px 1px rgba(16, 22, 26, 0.2)`
                          : null,
                        ':hover': {
                          ...styles[':hover'],
                          borderColor: state.isFocused ? SAIKU_COLOR : '#ccc'
                        }
                      }),
                      option: (styles, state) => ({
                        ...styles,
                        backgroundColor: state.isSelected ? SAIKU_COLOR : null,
                        ':hover': {
                          ...styles[':hover'],
                          backgroundColor: state.isSelected
                            ? SAIKU_COLOR
                            : '#e1ddff'
                        },
                        ':active': {
                          ...styles[':active'],
                          backgroundColor: state.isSelected ? SAIKU_COLOR : null
                        }
                      })
                    }}
                    isSearchable
                  />
                </FormGroup>
                <FormGroup label="Username" labelFor="username">
                  <InputGroup
                    name="username"
                    value={username}
                    onChange={this.handleChangeField}
                  />
                </FormGroup>
                <FormGroup label="Password" labelFor="password">
                  <PasswordMask
                    name="password"
                    inputClassName="bp3-input"
                    value={password}
                    onChange={this.handleChangeField}
                  />
                </FormGroup>
                {/*
                <FormGroup label="Security" labelFor="securityType">
                  <Select
                    name="securityType"
                    options={OPTIONS_SECURITY}
                    value={securityType}
                    onChange={value => this.setState({ securityType: value })}
                    styles={{
                      control: (styles, state) => ({
                        ...styles,
                        borderColor: state.isFocused ? SAIKU_COLOR : '#ccc',
                        boxShadow: state.isFocused
                          ? `0 0 0 1px ${SAIKU_COLOR}`
                          : null,
                        ':hover': {
                          ...styles[':hover'],
                          borderColor: state.isFocused ? SAIKU_COLOR : '#ccc'
                        }
                      }),
                      option: (styles, state) => ({
                        ...styles,
                        backgroundColor: state.isSelected ? SAIKU_COLOR : null,
                        ':hover': {
                          ...styles[':hover'],
                          backgroundColor: state.isSelected
                            ? SAIKU_COLOR
                            : '#e1ddff'
                        },
                        ':active': {
                          ...styles[':active'],
                          backgroundColor: state.isSelected ? SAIKU_COLOR : null
                        }
                      })
                    }}
                    isSearchable
                  />
                </FormGroup>
                */}
              </div>
              <div className={Classes.DIALOG_FOOTER}>
                <div className={Classes.DIALOG_FOOTER_ACTIONS}>
                  <Button
                    text="Advanced"
                    onClick={() => connType('advanced')}
                  />
                  <Button
                    type="submit"
                    text={!editMode ? 'Add' : 'Save'}
                    intent={Intent.DANGER}
                    loading={loading}
                  />
                  <Button text="Close" onClick={onClose} />
                </div>
              </div>
            </Fragment>
          )}
        </Validation>
      </Form>
    );
  }

  render() {
    const { loadingSchemas } = this.state;

    return !loadingSchemas ? (
      this.renderForm()
    ) : (
      <Loading className="m-t-20" size={30} center />
    );
  }
}

MondrianForm.propTypes = {
  editMode: PropTypes.bool,
  connType: PropTypes.func,
  getDataSources: PropTypes.func.isRequired,
  onClose: PropTypes.func.isRequired
};

MondrianForm.defaultProps = {
  editMode: false
};

export default MondrianForm;
